/*
 * $Id: mpz.c,v 1.5 2005-03-02 14:59:48 bacon Exp $
 */

#include <xp/mp/mpz.h>
#include <xp/c/memory.h>

xp_mpz_t* xp_mpz_init (xp_mpz_t* mpz)
{
	mpz->data = (xp_ulong_t*)xp_malloc (1);
	if (mpz->data == XP_NULL) {
		mpz->capacity = 0;
		mpz->size = 0;
		return XP_NULL;
	}

	mpz->capacity = 1;
	mpz->size = 0;
	return mpz;
}

void xp_mpz_deinit (xp_mpz_t* mpz)
{
	xp_free (mpz->data);
	mpz->data = XP_NULL;
	mpz->capacity = 0;
	mpz->size = 0;
}

xp_mpz_t* xp_mpz_resize (xp_mpz_t* mpz, xp_size_t capacity)
{
	xp_ulong_t* p;

	if (capacity < mpz->size) capacity = mpz->size;
	p = (xp_ulong_t*)xp_realloc (capacity);
	if (p == NULL) return XP_NULL;

	mpz->data = p;
	mpz->capacity = capacity;
	return mpz;
}

#define ADD_WITH_CARRY(v,c,x,y) \
	do { \
		v = x + y + c; \
		c = ((v) < (x) || ((v) == (x) && ((y) > 0 || (c) > 0)))? 1: 0; \
	} while (0)

xp_mpz_t* xp_mpz_add (xp_mpz_t* v, xp_mpz_t* x, xp_mpz_t* y)
{
	xp_ulong_t carry = 0;

	xp_size_t xsize = x->size;
	xp_size_t ysize = y->size;
	xp_size_t size, i;

	size = (xsize > ysize)? (xsize + 1): (ysize + 1);
	if (v->capacity < size) {
		if (xp_mpz_resize (v, size) == XP_NULL) return XP_NULL;
	}

	for (i = 0; i < size; i++) {
		if (xsize <= 0 && ysize <= 0) {
			ADD_WITH_CARRY (v->data[i], carry, 0, 0);
			continue;
		}
		else if (xsize <= 0) {
			ADD_WITH_CARRY (v->data[i], carry, 0, y->data[i]);
			continue;
		}
		else if (ysize <= 0) {
			ADD_WITH_CARRY (v->data[i], carry, x->data[i], 0);
			continue;
		}
		else {
			ADD_WITH_CARRY (v->data[i], carry, x->data[i], y->data[i]);
			xsize--; ysize--;
		}
	}
}

void xp_mpz_subtract (xp_mpz_t* v, xp_mpz_t* x, xp_mpz_t* y)
{
}
